package controler.algorithms.ghsom;

import java.util.LinkedList;
import java.util.Random;
/** 
 * @file Neuron.java  
 * 
 **/

/**
 *    Neuron sieci.
 *  
 *  Klasa Neuron
 */
public class Neuron implements INeuron{
	int weightsize;
	NeuronLayer nl;
	LinkedList<double[]> representingDataItems;
	int maplevel,mapspx,mapspy;
	double MQE;
	double[] weights;
	Random rnd;
	
	/**
	 *   Konstruktor klasy nowego Neuronu
	 * 
	 * @param w - wektor wag neuronu
	 * @param inlevel - poziom na jakim znajduje sie neuron
	 * @param spx - polozenie neuronu x na mapie
	 * @param spy - polozenie neuronu y na mapie
	 * 
	 */
	public Neuron(double[] w,int inlevel,int spx,int spy)
	{
		  weights = w;
		  weightsize = weights.length;
		  representingDataItems = new LinkedList<double[]>();
		  nl = null;
		  maplevel=inlevel;
		  mapspx=spx;
		  mapspy=spy;
	}
	
	/**
	 *   Konstruktor klasy nowego Neuronu - przydziela losowo wagi
	 * 
	 * @param ws - rozmiar wektoru wag
	 * @param inlevel - poziom na jakim znajduje sie neuron
	 * @param spx - polozenie neuronu x na mapie
	 * @param spy - polozenie neuronu y na mapie
	 *  
	 */
	public Neuron(int ws,int inlevel,int spx,int spy)
	{
		  weightsize = ws;
		  rnd = new Random();
		  weights = new double[weightsize];
		  for (int i=0;i<weightsize;i++) 
		  {
		    weights[i] = rnd.nextDouble();
		  }
		  nl = null;
		  weights = Globals.normVec(weights);
		  representingDataItems = new LinkedList<double[]>();
		  maplevel=inlevel;
		  mapspx=spx;
		  mapspy=spy;
	}
	
	/**
	 *    Metoda adaptujaca wektor wag dla danego neuronu
	 * 
	 *  @param di - aktualny wektor wejsciowy
	 *  @param dist - odleglosc
	 *  @param learnrate - wsp. uczenia
	 *  @param neighbourhood - sasiedztwo
	 *  
	 */
	@Override
	public void adaptWeights(double[] di, double dist, double learnrate, double neighbourhood) {
		double n_influence = learnrate * (double)Math.exp(-1.0*Math.pow((dist/(2.0*Math.pow((double)neighbourhood,2))),2));    
		  
		for (int i=0;i<weightsize;i++) {
		    weights[i] = weights[i] + (n_influence * (di[i] - weights[i]));
		}
	}

	/**
	 *    Metoda dodajaca nowa mape neuronow
	 * 
	 *  @param sn - Super Neuron
	 *  @param MQE
	 *  @param level
	 *  @param posX
	 *  @param posY
	 *  @param ULweight
	 *  @param URweight
	 *  @param LLweight
	 *  @param LRweight
	 *  
	 */
	@Override
	public void addMap(Neuron sn, double MQE, int level, int posX, int posY,
			double[] ULweight, double[] URweight, double[] LLweight, double[] LRweight) {
		nl = new NeuronLayer(sn,representingDataItems,MQE,level,2,2,posX,posY,ULweight,URweight,LLweight,LRweight);
	}

	/**
	 *    Metoda dodajaca nowa mape neuronow
	 * 
	 *  @param sn
	 *  @param MQE
	 *  @param level
	 *  @param sizeX
	 *  @param sizeY
	 *  @param posX
	 *  @param posY
	 *  
	 */
	@Override
	public void addMap(Neuron sn, double MQE, int level, int sizeX, int sizeY,
			int posX, int posY) {
		nl = new NeuronLayer(sn,representingDataItems,MQE,level,sizeX,sizeY,posX,posY,null,null,null,null);
	}

	/**
	 *    Metoda dodajaca nowa mape neuronow
	 * 
	 *  @param dataItems
	 *  @param MQE
	 *  @param level
	 *  @param sizeX
	 *  @param sizeY
	 *  @param posX
	 *  @param posY
	 *   
	 */
	@Override
	public void addMap(LinkedList<double[]> dataItems, double MQE, int level,
			int sizeX, int sizeY, int posX, int posY) 
	{
		nl = new NeuronLayer(null,dataItems,MQE,level,sizeX,sizeY,posX,posY,null,null,null,null);
	}

	/**
	 *    Metoda dodajaca nowy element do wektora danych danego neuronu
	 * 
	 *  @param di - element danych
	 *   
	 */
	@Override
	public void addRepresentingDataItem(double[] di) {
		representingDataItems.add(di);
	}

	/**
	 *    Metoda wyznaczajaca odleglosc miedzy wektorem wejsciowy, a wagami sieci
	 * 
	 *  @param di - wektor wejsciowy
	 * 
	 *  @return Dystans miedzy dwoma wektorami
	 */
	@Override
	public double calcDist(double[] di) 
	{
		return Globals.calcQE(di,weights);
	}

	/**
	 *    Metoda obliczajaca blad sieci
	 *  
	 */
	@Override
	public void calcMQE() {
		  if (representingDataItems.size()==0) 
		  {
			    MQE = 0;
		  } 
		  else 
		  {
			MQE = 0;
   
			for (int i=0; i<representingDataItems.size();i++) 
			{
			      MQE += Globals.calcQE(representingDataItems.get(i),weights);
			}
		  }
	}


	/**
	 *    Metoda czyszczaca wektor danych 
	 *   
	 */
	@Override
	public void clearRepresentingDataItems() {
		representingDataItems.clear();
	}

	/**
	 *    Metoda pobierajaca warstwe neuronow
	 * 
	 *  @return Warstwa neuronow
	 */
	@Override
	public NeuronLayer getMap() {
		return nl;
	}

	/**
	 *    Metoda pobierajaca liste danych dla danego neuronu
	 * 
	 *  @return Lista danych danego neuronu
	 */
	@Override
	public LinkedList<double[]> getRepresentingDataItems() {
		return representingDataItems;
	}
	
	/**
	 *    Metoda zwracajaca rozmiar wektora danych
	 * 
	 *  @return Rozmiar wektora
	 */
	@Override
	public int representsMultiDataItems() {
		return representingDataItems.size();
	}
}
